﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using PubSubLite.Configuration;
using PubSubLite.Messaging.Threading;
using System.Collections;
using PubSubLite.Utils;

namespace PubSubLite.Messaging
{
    internal class CommandDispatcher : ICommandDispatcher
    {
        public CommandDispatcher(IBusConfiguration configuration, IMessageChannel channel)
            : this(configuration, channel, SubscribersStorage.Default)
        {

        }

        public CommandDispatcher(IBusConfiguration configuration, IMessageChannel channel, ISubscribersStorage subscribers)
        {
            Require.Argument.IsNotNull(configuration, "configuration");
            Require.Argument.IsNotNull(channel, "channel");
            Require.Argument.IsNotNull(subscribers, "subscribers");
            _channel = channel;
            _subscribersStorage = subscribers;
            _configuration = configuration;
        }

        private IMessageChannel _channel;
        private ISubscribersStorage _subscribersStorage;
        private IBusConfiguration _configuration;

        private class CallBackItem<T>
        {
            private Action<T> _callback;

            public void SetCallBack(Action<T> action)
            {
                _callback = action;
            }

            public Action<T> GetCallBack()
            {
                return _callback;
            }
        }

        void ICommandDispatcher.DispatchReturnMessage(IMessageBase message, Type messageDataType)
        {
            var callback = _subscribersStorage.GetCommandCallback(message.CorrelatingMessageID);
            var data = message.GetType().GetInterface("IMessage`1").GetProperty("Data").GetValue(message, null);
            var invokeArgs = new InvokeArgs(callback, data);
            var invoker = new Invoker(_configuration);
            _configuration.Debug("dispatching command to callback, messageID: " + message.MessageID.ToString());
            invoker.Invoke(invokeArgs);
        }

        void ICommandDispatcher.DispatchCommand(IMessageBase message, Type commandDataType)
        {
            var subscriptions = new ArrayList(_subscribersStorage.GetSubscribersByType(commandDataType));
            if (subscriptions != null && subscriptions.Count > 0)
            {
                var subscriber = subscriptions[0];
                var data = message.GetType().GetInterface("IMessage`1").GetProperty("Data").GetValue(message, null);
                var subscriptionType = subscriber.GetType().GetInterface("ISubscription`1");
                var callback = subscriptionType.GetMethod("Notify");
                var invokeArgs = new InvokeArgs(subscriber, callback, new object[] { message });
                var invoker = new Invoker(_configuration);
                _configuration.Debug("dispatching command from channel, messageID:" + message.MessageID.ToString());
                invoker.Invoke(invokeArgs);
            }
            else
            {

                _configuration.LogWarning(string.Format("No command subscribers found for {0}", commandDataType.Name));
            }
        }
    }
}
